package com.xms.sdk.service;

import com.xms.core.enums.AccessRightValue;
import com.xms.core.model.PagedList;
import com.xms.schema.core.AttributeTypeIds;
import com.xms.schema.entity.*;
import com.xms.schema.extension.AttributeExt;
import com.xms.schema.service.entity.IEntityFinderService;
import com.xms.schema.service.stringmap.IStringMapFinderService;
import com.xms.sdk.core.DynamicData;
import com.xms.sdk.core.RetrieverParameter;
import com.xms.sdk.core.query.*;
import com.xms.sdk.data.IOrganizationDataRetriever;
import com.xms.sdk.extensions.IQueryMetadataFinder;
import com.xms.sdk.extensions.QueryExpressionExt;
import com.xms.sdk.query.QueryMetadataDescriptor;
import com.xms.sdk.query.QueryResult;
import com.xms.security.identity.ICurrentUser;
import com.xms.security.service.dataauth.IRoleEntityPermissionService;
import com.xms.security.service.principal.ISystemUserPermissionService;
import com.xms.utils.CollectionUtil;
import com.xms.utils.JsonUtil;
import com.xms.utils.RegexUtil;
import com.xms.utils.StringUtil;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 数据查询服务
 * </p>
 *
 * @author migo
 * @since 2021/9/7
 */
@Service
public class FetchDataService implements IFetchDataService {
    private IRoleEntityPermissionService _roleEntityPermissionService;
    private IEntityFinderService _entityFinderService;
    private IStringMapFinderService _stringMapFinderService;
    private ISystemUserPermissionService _systemUserPermissionService;
    private IQueryMetadataFinder _queryMetadataFinder;
    private IOrganizationDataRetriever _organizationDataRetriever;
    private ICurrentUser _currentUser;

    @Autowired
    public void FetchDataService(IRoleEntityPermissionService roleEntityPermissionService,
                                 IEntityFinderService entityFinderService,
                                 IStringMapFinderService stringMapFinderService,
                                 ISystemUserPermissionService systemUserPermissionService,
                                 IQueryMetadataFinder queryMetadataFinder,
                                 IOrganizationDataRetriever organizationDataRetriever,
                                 ICurrentUser currentUser) {
        _roleEntityPermissionService = roleEntityPermissionService;
        _entityFinderService = entityFinderService;
        _stringMapFinderService = stringMapFinderService;
        _systemUserPermissionService = systemUserPermissionService;
        _queryMetadataFinder = queryMetadataFinder;
        _organizationDataRetriever = organizationDataRetriever;
        _currentUser = currentUser;
    }

    private List<Attribute> getNonePermissionFields(List<Attribute> attributes) {
        List<UUID> securityFields = attributes.stream().filter(n -> n.getAuthorizationenabled() == true).map(n -> n.getAttributeid()).collect(Collectors.toList());
        //无权限的字段
        if (!securityFields.isEmpty()) {
            List<UUID> noneReadFields = !securityFields.isEmpty() ? _systemUserPermissionService.getNoneReadFields(_currentUser.getUser().SystemUserId, securityFields) : null;
            return attributes.stream().filter(x -> noneReadFields.contains(x.getAttributeid())).collect(Collectors.toList());
        }
        return null;
    }

    @Override
    public PagedList<DynamicData> execute(FetchDescriptor fetch, boolean wrapOptionName) throws Exception {
        fetch.query = toQueryExpression(fetch.fetchConfig);
        fetch.query.PageInfo = new PagingInfo();
        fetch.query.PageInfo.PageNumber = fetch.page;
        fetch.query.PageInfo.PageSize = fetch.pageSize;
        RetrieverParameter parameters = RetrieverParameter.build(fetch.query, _currentUser);
        if (fetch.user != null && !fetch.user.isAdministrator() && CollectionUtil.notEmpty(fetch.user.getRoles())) {
            List<UUID> roles = fetch.user.getRoles();
            List<String> entities = QueryExpressionExt.getAllEntityNames(fetch.query, null);
            List<UUID> entIds = _entityFinderService.getByNames(entities).stream().map(x -> x.getEntityid()).collect(Collectors.toList());
            parameters.setEntityPermissions(_roleEntityPermissionService.getRolePermissions(entIds, roles, AccessRightValue.Read));
        }
        //元数据
        QueryMetadataDescriptor metadataDescriptor = _queryMetadataFinder.getAll(fetch.query);
        //搜索关键字
        if (StringUtil.notEmpty(fetch.keyword)) {
            if (fetch.query.Criteria == null || (CollectionUtil.isEmpty(fetch.query.Criteria.Conditions) && CollectionUtil.isEmpty(fetch.query.Criteria.Filters))) {
                fetch.query.Criteria = new FilterExpression(LogicalOperator.And);
            } else {
                fetch.query.Criteria.FilterOperator = LogicalOperator.And;
            }
            if (StringUtil.notEmpty(fetch.field)) {
                makeFilterByKeyword(metadataDescriptor, fetch.query, fetch.keyword, fetch.field);
            } else {
                makeFilterByKeyword(metadataDescriptor, fetch.query, fetch.keyword);
            }
        }
        //过滤
        if (fetch.filter != null && (CollectionUtil.notEmpty(fetch.filter.Filters) || CollectionUtil.notEmpty(fetch.filter.Conditions))) {
            fetch.query.Criteria.addFilter(fetch.filter);
        }
        //排序
        if (fetch.sort != null) {
            //重置排序
            fetch.query.Orders.clear();
            fetch.query.AddOrder(fetch.sort.name, fetch.sort.sortAscending ? OrderType.Ascending : OrderType.Descending);
        }
        //无指定排序时，默认第一列
        if (CollectionUtil.isEmpty(fetch.query.Orders)) {
            if (metadataDescriptor.attributeList.stream().anyMatch(n -> n.getName().equalsIgnoreCase("createdon"))) {
                fetch.query.AddOrder("createdon", OrderType.Descending);
            } else {
                fetch.query.AddOrder(fetch.query.ColumnSet.Columns.get(0), OrderType.Descending);
            }
        }

        PagedList<DynamicData> result;
        parameters.setQuery(fetch.query).setIgnorePermissions(false).setNoneReadFields(getNonePermissionFields(metadataDescriptor.attributeList));
        if (fetch.getAll) {
            QueryResult<List<DynamicData>> queryResult = _organizationDataRetriever.dynamicList(parameters);
            result = new PagedList<>();
            result.items = queryResult.data;
        } else {
            QueryResult<PagedList<DynamicData>> queryResult = _organizationDataRetriever.dynamicPaged(parameters);
            result = queryResult.data;
        }
        if (wrapOptionName) {
            result.items = wrapOptionName(metadataDescriptor, result.items, fetch.query);
        }

        return result;
    }

    @Override
    public PagedList<DynamicData> execute(QueryExpression query, boolean wrapOptionName) throws Exception {
        RetrieverParameter parameters = RetrieverParameter.build(query, _currentUser).setIgnorePermissions(false).setNoneReadFields(getNonePermissionFields(_queryMetadataFinder.getAttributes(query)));
        if (this._currentUser != null && !this._currentUser.isAdministrator() && CollectionUtil.notEmpty(this._currentUser.getRoles())) {
            List<UUID> roles = this._currentUser.getRoles();
            List<String> entities = QueryExpressionExt.getAllEntityNames(query);
            List<UUID> entIds = _entityFinderService.getByNames(entities).stream().map(n -> n.getEntityid()).collect(Collectors.toList());
            parameters.setEntityPermissions(_roleEntityPermissionService.getRolePermissions(entIds, roles, AccessRightValue.Read));
        }
        if (query.PageInfo == null || query.PageInfo.PageSize < 1) {
            QueryResult<List<DynamicData>> queryResult = _organizationDataRetriever.dynamicList(parameters);
            List<DynamicData> datas = queryResult.data;
            if (wrapOptionName) {
                datas = wrapOptionName(queryResult.metadataDescriptor, datas, query);
            }
            PagedList<DynamicData> result = new PagedList<DynamicData>();
            result.items = datas;
            result.totalitems = datas.size();
            return result;
        } else {
            QueryResult<PagedList<DynamicData>> queryResult = _organizationDataRetriever.dynamicPaged(parameters);
            PagedList<DynamicData> result = queryResult.data;
            if (wrapOptionName) {
                result.items = wrapOptionName(queryResult.metadataDescriptor, result.items, query);
            }
            return result;
        }
    }

    @Override
    public PagedList<DynamicData> execute(FetchDescriptor fetch) throws Exception {
        return this.execute(fetch, false);
    }

    @Override
    public PagedList<DynamicData> execute(QueryExpression query) throws Exception {
        return this.execute(query, false);
    }

    private int getConditionOperator(Attribute attr) {
        switch (attr.getAttributetypename()) {
            case AttributeTypeIds.NVARCHAR:

            case AttributeTypeIds.VARCHAR:
                return ConditionOperator.Like;

            default:
                return ConditionOperator.Equal;
        }
    }

    private String getSearchName(Attribute attr, String entityAlias) {
        String name = attr.getName();
        if (attr != null && (AttributeExt.typeIsPrimaryKey(attr) || AttributeExt.typeIsRef(attr))) {
            name += "name";
        }
        return entityAlias.isEmpty() ? name : entityAlias + "." + name;
    }

    private ConditionExpression getCondition(Attribute attr, Object keyword) {
        return this.getCondition(attr, keyword, "");
    }

    private ConditionExpression getCondition(Attribute attr, Object keyword, String entityAlias) {
        //数字类型
        if (RegexUtil.isDigit(keyword.toString()) && (AttributeExt.typeIsInt(attr)
                || AttributeExt.typeIsFloat(attr)
                || AttributeExt.typeIsDecimal(attr)
                || AttributeExt.typeIsMoney(attr)
                || AttributeExt.typeIsSmallMoney(attr)
                || AttributeExt.typeIsSmallInt(attr)
        )) {
            return new ConditionExpression(getSearchName(attr, entityAlias), getConditionOperator(attr), keyword);
        }
        //日期类型
        else if ((AttributeExt.typeIsDateTime(attr)
                || AttributeExt.typeIsSmallDateTime(attr)
        )) {
            DateTime d = DateTime.parse(keyword.toString());
            return new ConditionExpression(getSearchName(attr, entityAlias), getConditionOperator(attr), keyword);
        }
        //guid类型
        else if (AttributeExt.typeIsRef(attr)) {
            if (RegexUtil.isUUID(keyword.toString())) {
                return new ConditionExpression(attr.getName(), getConditionOperator(attr), keyword);
            }
            return new ConditionExpression(getSearchName(attr, entityAlias), ConditionOperator.Like, keyword);
        }
        //选项类型
        else if ((AttributeExt.typeIsState(attr)
                || AttributeExt.typeIsBit(attr)
                || AttributeExt.typeIsPickList(attr) || AttributeExt.typeIsStatus(attr))) {
            if (RegexUtil.isDigit(keyword.toString()))//如果是数值
            {
                return new ConditionExpression(entityAlias.isEmpty() ? attr.getName() : entityAlias + "." + attr.getName(), getConditionOperator(attr), keyword);
            } else {
                //按名称查找选项值
                //...
                if (AttributeExt.typeIsState(attr) || AttributeExt.typeIsBit(attr)) {
                    if (attr.getPicklists().isEmpty()) {
                        attr.setPicklists(_stringMapFinderService.getByAttributeid(attr.getAttributeid()));
                    }
                    Object finalKeyword = keyword;
                    Stream<StringMap> filter = attr.getPicklists().stream().filter(n -> n.getName().equalsIgnoreCase(finalKeyword.toString()));
                    if (filter.count() > 0) {
                        keyword = filter.findFirst().get().getValue();
                    }
                } else if (AttributeExt.typeIsPickList(attr) || AttributeExt.typeIsStatus(attr)) {
                    if (attr.getOptionset() == null) {
                        attr.setOptionset(new OptionSet());
                    }
                    Object finalKeyword = keyword;
                    Stream<OptionSetDetail> filter = attr.getOptionset().getItems().stream().filter(n -> n.getName().equalsIgnoreCase(finalKeyword.toString()));
                    if (filter.count() > 0) {
                        keyword = filter.findFirst().get().getValue();
                    }
                }
            }
        }
        //字符串类型
        else if ((AttributeExt.typeIsNvarchar(attr)
                || AttributeExt.typeIsVarchar(attr)
                || AttributeExt.typeIsChar(attr))) {
            return new ConditionExpression(getSearchName(attr, entityAlias), getConditionOperator(attr), keyword);
        }
        return null;
    }

    // 根据关键字生成过滤条件
    private void makeFilterByKeyword(QueryMetadataDescriptor metadataDescriptor, QueryExpression queryExpression, String keyword, String... fields) {
        FilterExpression filter = new FilterExpression(LogicalOperator.Or);
        int i = 1;
        if (CollectionUtil.notEmpty(fields)) {
            List<String> mainQFields = Arrays.stream(fields).filter(n -> n.indexOf('.') < 0).collect(Collectors.toList());
            if (CollectionUtil.notEmpty(mainQFields)) {
                for (String column : queryExpression.ColumnSet.Columns)//主表
                {
                    if (!mainQFields.contains(column)) {
                        if (i > mainQFields.size()) {
                            break;
                        }

                        continue;
                    }
                    Attribute attr = metadataDescriptor.attributeList.stream().filter(x -> x.getName().equalsIgnoreCase(column) && x.getEntityname().equalsIgnoreCase(queryExpression.EntityName)).findFirst().get();
                    filter.addCondition(getCondition(attr, keyword));
                    i++;
                }
            }
        } else {
            for (String column : queryExpression.ColumnSet.Columns)//主表
            {
                Attribute attr = metadataDescriptor.attributeList.stream().filter(x -> x.getName().equalsIgnoreCase(column) && x.getEntityname().equalsIgnoreCase(queryExpression.EntityName)).findFirst().get();
                filter.addCondition(getCondition(attr, keyword));
                i++;
            }
        }
        if (i > 1) {
            queryExpression.Criteria.addFilter(filter);
        }
        if (CollectionUtil.notEmpty(fields) && i > fields.length)//如果在主实体已匹配完则返回
        {
            return;
        }
        if (CollectionUtil.notEmpty(queryExpression.LinkEntities)) {
            for (LinkEntity le : queryExpression.LinkEntities)//关联表
            {
                List<String> leColumns = QueryExpressionExt.getAllColumns(queryExpression, le, metadataDescriptor.attributeList, true, "");
                List<String> linkQFields = leColumns.stream().filter(n -> n.indexOf('.') > 0 && n.split(".")[0].equalsIgnoreCase(le.EntityAlias)).collect(Collectors.toList());
                if (CollectionUtil.isEmpty(linkQFields)) {
                    continue;
                }

                for (String column : linkQFields) {
                    String field = column.split(".")[1];
                    Stream<Attribute> sfilter = metadataDescriptor.attributeList.stream().filter(x -> x.getName().equalsIgnoreCase(field) && x.getEntityname().equalsIgnoreCase(le.LinkToEntityName));
                    if (sfilter.count() == 0) {
                        continue;
                    }
                    Attribute attr = sfilter.findFirst().get();
                    filter.addCondition(getCondition(attr, keyword, le.EntityAlias));
                }
            }
        }
    }

    // 根据关键字生成过滤条件
    private void makeLinkEntityFilterByKeyword(QueryMetadataDescriptor metadataDescriptor, QueryExpression queryExpression, LinkEntity linkEntity, String keyword, String... fields) {
        if (linkEntity.LinkCriteria == null
                || (linkEntity.LinkCriteria.Conditions.isEmpty() && linkEntity.LinkCriteria.Filters.isEmpty())) {
            linkEntity.LinkCriteria = new FilterExpression(queryExpression.Criteria != null && queryExpression.LinkEntities.indexOf(linkEntity) == 0 ? queryExpression.Criteria.FilterOperator : LogicalOperator.Or);
        }
        FilterExpression linkFilter = new FilterExpression(LogicalOperator.Or);
        int i = 0;
        for (String column : linkEntity.Columns.Columns) {
            if (CollectionUtil.notEmpty(fields) && !Arrays.stream(fields).anyMatch(x -> x.contains(column))) {
                continue;
            }
            Attribute attr = metadataDescriptor.attributeList.stream().filter(x -> x.getName().equalsIgnoreCase(column) && x.getEntityname().equalsIgnoreCase(linkEntity.LinkToEntityName)).findFirst().get();
            linkFilter.addCondition(getCondition(attr, keyword));
            i++;
        }
        if (i > 0) {
            linkEntity.LinkCriteria.addFilter(linkFilter);
        }
        if (linkEntity.LinkEntities.size() > 0) {
            for (LinkEntity le : queryExpression.LinkEntities) {
                makeLinkEntityFilterByKeyword(metadataDescriptor, queryExpression, le, keyword, fields);
            }
        }
    }

    // 为数据添加选项名称
    private List<DynamicData> wrapOptionName(QueryMetadataDescriptor metadataDescriptor, List<DynamicData> datas, QueryExpression queryExpression) {
        List<Attribute> optionAttributes = metadataDescriptor.attributeList.stream().filter(n -> AttributeExt.typeIsBit(n) || AttributeExt.typeIsState(n) || AttributeExt.typeIsPickList(n)).collect(Collectors.toList());
        if (CollectionUtil.isEmpty(optionAttributes)) {
            return datas;
        }
        Entity mainEntity = metadataDescriptor.entityList.stream().filter(x -> x.getName().equalsIgnoreCase(queryExpression.EntityName)).findFirst().get();
        for (Attribute attr : optionAttributes) {
            if (AttributeExt.typeIsPickList(attr) || AttributeExt.typeIsStatus(attr)) {
                for (DynamicData _this : datas) {
                    String columnName = attr.getName();
                    if (!attr.getEntityid().equals(mainEntity.getEntityid())) {
                        LinkEntity le = QueryExpressionExt.findLinkEntityByName(queryExpression, attr.getEntityname());
                        if (le != null) {
                            columnName = le.EntityAlias + "." + columnName;
                        }
                    }
                    if (_this.containsKey(columnName + "name")) {
                        break;
                    }
                    Object value = _this.get(columnName);
                    if (value != null) {
                        Stream<OptionSetDetail> oft = attr.getOptionset().getItems().stream().filter(n -> n.getValue() == (int) value);
                        if (oft.count() > 0) {
                            OptionSetDetail o = oft.findFirst().get();
                            _this.put(columnName + "name", o != null ? o.getName() : "");
                        }
                    } else {
                        _this.put(columnName + "name", "");
                    }
                }
            } else if (AttributeExt.typeIsState(attr) || AttributeExt.typeIsBit(attr)) {
                for (DynamicData _this : datas) {
                    String columnName = attr.getName();
                    if (!attr.getEntityid().equals(mainEntity.getEntityid())) {
                        LinkEntity le = QueryExpressionExt.findLinkEntityByName(queryExpression, attr.getEntityname());
                        if (le != null) {
                            columnName = le.EntityAlias + "." + columnName;
                        }
                    }
                    if (_this.containsKey(columnName + "name")) {
                        break;
                    }
                    Object value = _this.get(columnName);
                    if (value != null) {
                        int bVal = RegexUtil.isDigit(value.toString()) ? Integer.parseInt(value.toString()) : (Boolean.parseBoolean(value.toString()) ? 1 : 0);
                        Stream<StringMap> oft = attr.getPicklists().stream().filter(n -> n.getValue() == bVal);
                        if (oft.count() > 0) {
                            StringMap o = oft.findFirst().get();
                            _this.put(columnName + "name", o != null ? o.getName() : "");
                        }
                    } else {
                        _this.put(columnName + "name", "");
                    }
                }
            }
        }
        return datas;
    }

    private QueryExpression toQueryExpression(String fetchConfig) {
        return JsonUtil.toBean(fetchConfig, QueryExpression.class);
    }
}
